﻿using CPPEI.Coolzon.Common;
using CPPEI.Coolzon.Service;
using CPPEI.Coolzon.Service.UpdateService;
using CPPEI.Coolzon.UpdateServer;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace CPPEI.Coolzon.UpdateServer
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class UpdateService : IUpdateService
    {
        /// <summary>
        /// 获取计划更新的文件
        /// </summary>
        /// <param name="appType"></param>
        /// <param name="appVersion"></param>
        /// <returns></returns>
        public Service.UpdateService.UpdateSchedule GetUpdateSchedule(string product,string appType, string appVersion)
        {
            Service.UpdateService.UpdateSchedule result = new UpdateSchedule();
            try
            {
                Logger.InfoToTag("UpdateServer", string.Format("{0} localVersion:{1}开始更新", appType, appVersion));

                string targetVersion = "";
                XmlDocument xmlUpdateFile = new XmlDocument();
                string scheduleFilePath = CZEnvironment.GetBaseDirectory() + string.Format("Patchs\\{0}\\{1}\\version.xml", product, appType);
                if (System.IO.File.Exists(scheduleFilePath))
                {
                    xmlUpdateFile.Load(scheduleFilePath);
                    targetVersion = xmlUpdateFile.SelectSingleNode("Version/LatestVersion").InnerText;
                }
                else
                {
                    result.IsSuccess = false;
                    result.Message = "当前程序版本，已是最新版本!";
                    return result;
                }

                Version vApp = new Version("0.0.0.0");
                if (!string.IsNullOrEmpty(appVersion))
                {
                    
                    vApp = Version.Parse(appVersion);
                }
                Version vTarget = new Version("0.0.0.0");
                if (!string.IsNullOrEmpty(targetVersion))
                {
                    vTarget = Version.Parse(targetVersion);
                }

                if(vTarget == vApp)
                {
                    result.Files = new List<UpdateFile>();
                    result.IsSuccess = false;
                    result.Message = "当前程序版本，已是最新版本!";
                    return result;
                }
                else if (vApp > vTarget)
                {
                    result.IsSuccess = false;
                    result.Files = new List<Service.UpdateService.UpdateFile>();
                    result.Message = "本地程序版本高于更新服务中的版本!";
                    return result;
                }
                else if (vTarget > vApp)
                {
                    result = new Service.UpdateService.UpdateSchedule();
                    string updateFilesIncrementalPath = GetXmlFilePath(CZEnvironment.GetBaseDirectory(), appVersion, appType, targetVersion, product);
                    string updateFileFullPath = GetXmlFilePath(CZEnvironment.GetBaseDirectory(), "Full", appType, targetVersion, product);
                    if (System.IO.File.Exists(updateFilesIncrementalPath))
                    {
                        //增量式补丁
                        result.Files = GetUpdateFiles(updateFilesIncrementalPath,GetFileName(appVersion,targetVersion));
                        result.FromVersion = appVersion;
                        result.TargetVersion = targetVersion;
                        result.IsNecessary = GetIsNecessaryValue(updateFilesIncrementalPath);
                        result.Mode = "Incremental";
                        result.IsSuccess = true;
                        result.Message = "获取增量式补丁成功!";
                        Logger.InfoToTag("UpdateServer", "更新完成");
                    }
                    else if (System.IO.File.Exists(updateFileFullPath))
                    {
                        //完整补丁
                        result.Files = GetUpdateFiles(updateFileFullPath,GetFileName("Full",targetVersion));
                        result.FromVersion = appVersion;
                        result.TargetVersion = targetVersion;
                        result.Mode = "Full";
                        result.AppType = appType;
                        result.IsNecessary = GetIsNecessaryValue(updateFileFullPath);
                        result.IsSuccess = true;
                        result.Message = "获取完整补丁成功!";
                        Logger.InfoToTag("UpdateServer", "更新完成");
                    }
                    else
                    {
                        return null;
                    }
                }
                return result;
            }
            catch (Exception ex)
            {
                Logger.ErrorToTag("UpdateServer", "{0}{1}", ex.Message, ex.StackTrace);
            }
            return result;
        }

        /// <summary>
        /// 获取当前更新包，是否为必须更新
        /// </summary>
        /// <param name="dir"></param>
        /// <returns></returns>
        private bool GetIsNecessaryValue(string dir)
        {
            if (System.IO.File.Exists(dir))
            {
                XmlDocument xmlFile = new XmlDocument();
                xmlFile.Load(dir);
                XmlNode patchXmlNode = xmlFile.SelectSingleNode("Patch");
                if (patchXmlNode != null)
                {
                    if (patchXmlNode.Attributes["IsNecessary"] != null)
                    {
                        string IsNecessary = patchXmlNode.Attributes["IsNecessary"].Value;
                        if (!string.IsNullOrEmpty(IsNecessary))
                        {
                            return Convert.ToBoolean(IsNecessary);

                        }
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// 获取将要更新的Xml文件的路径
        /// </summary>
        /// <param name="directory">更新包所在路径</param>
        /// <param name="appVersion">起始版本</param>
        /// <param name="appType">应用程序类型</param>
        /// <param name="targetVersion">目标版本</param>
        /// <param name="product">产品名称</param>
        /// <returns></returns>
        private string GetXmlFilePath(string directory, string appVersion, string appType, string targetVersion, string product)
        {
            string xmlFilePath = string.Format("Patchs\\{0}\\{1}\\Patch_{2}-{3}\\Patch_{4}-{5}.xml", product, appType, appVersion, targetVersion, appVersion, targetVersion);
            return directory + xmlFilePath;
        }

        /// <summary>
        /// 获取将要更新的文件的绝对路径
        /// </summary>
        /// <param name="directory"></param>
        /// <param name="product"></param>
        /// <param name="appType"></param>
        /// <param name="formVersion"></param>
        /// <param name="scheduleVersion"></param>
        /// <param name="fileName"></param>
        /// <param name="parentFolder"></param>
        /// <returns></returns>
        private string GetFilePath(string directory,string product,string appType,string formVersion,string scheduleVersion,string fileName,string parentFolder)
        {
            string filePath = string.Format("Patchs\\{0}\\{1}\\Patch_{2}-{3}{4}\\{5}", product, appType, formVersion, scheduleVersion, parentFolder, fileName);
            return directory + filePath;
        }

        /// <summary>
        /// 获取将要更新的xml文件名
        /// </summary>
        /// <param name="appVersion">起始版本</param>
        /// <param name="targetVersion">目标版本</param>
        /// <returns></returns>
        private string GetFileName(string appVersion, string targetVersion)
        {
            return string.Format("Patch_{0}-{1}.xml", appVersion, targetVersion);
        }

        /// <summary>
        /// 更新单个文件
        /// </summary>
        /// <param name="appType"></param>
        /// <param name="scheduleMode"></param>
        /// <param name="scheduleVersion"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public Service.UpdateService.UpdateResult UpdateFile(string product, string appType, string scheduleMode, string formVersion, string scheduleVersion, string fileName)
        {
            Logger.Info(this.GetType().Name, string.Format("计划版本{0},开始更新程序文件{1}", scheduleVersion, fileName));
            Service.UpdateService.UpdateResult updateResult = new Service.UpdateService.UpdateResult();

            string firstFilePath = "";
            string strSub = fileName.Substring(0, 1);
            if (strSub == ".")
            {
                fileName = fileName.Replace(".\\", "");
            }
            else if (strSub == "~")
            {
                fileName = fileName.Replace("~\\", "");
                if (scheduleMode == "Full")
                {
                    firstFilePath = GetFilePath(CZEnvironment.GetBaseDirectory(), product, appType,scheduleMode, scheduleVersion, fileName, string.Empty);
                }
                else
                {
                    firstFilePath = GetFilePath(CZEnvironment.GetBaseDirectory(), product, appType, formVersion, scheduleVersion, fileName, string.Empty);
                }
            }

            string filePath = "";
            if (scheduleMode == "Full")
            {
                filePath =GetFilePath(CZEnvironment.GetBaseDirectory(),product,appType,scheduleMode,scheduleVersion,fileName,"\\Files");
            }
            else if (scheduleMode == "Incremental")
            {
                filePath =GetFilePath(CZEnvironment.GetBaseDirectory(),product,appType,formVersion,scheduleVersion,fileName,"\\Files");
            }

            if (!string.IsNullOrEmpty(firstFilePath))
            {
                return GetUpdateResult(firstFilePath, fileName);
            }
            else
            {
                return GetUpdateResult(filePath, fileName);
            }

        }


        private Service.UpdateService.UpdateResult GetUpdateResult(string filePath, string fileName)
        {
            Service.UpdateService.UpdateResult updateResult = new Service.UpdateService.UpdateResult();

            if (System.IO.File.Exists(filePath))
            {

                FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                try
                {
                    updateResult.Data = new byte[fs.Length];
                    fs.Seek(0, SeekOrigin.Begin);
                    fs.Read(updateResult.Data, 0, (int)fs.Length);
                    updateResult.IsSuccess = true;
                    updateResult.Message = string.Format("文件{0}更新成功！", fileName);
                    updateResult.Size = fs.Length;

                    Logger.InfoToTag("UpdateServer", string.Format("程序文件{0}更新成功", fileName));
                    return updateResult;
                }
                catch (Exception ex)
                {
                    updateResult.IsSuccess = false;
                    updateResult.Message = ex.Message;
                    Logger.ErrorToTag("UpdateServer", string.Format("程序文件{0}更新失败:{1}", fileName, ex));
                }
                finally
                {

                    fs.Close();

                }
            }
            return updateResult;
        }

        /// <summary>
        /// 根据补丁地址获取文件信息
        /// </summary>
        /// <param name="dir"></param>
        /// <returns></returns>
        private List<Service.UpdateService.UpdateFile> GetUpdateFiles(string dir, string xmlPathName)
        {
            List<Service.UpdateService.UpdateFile> updateFiles = new List<Service.UpdateService.UpdateFile>();

            UpdateFile firstUpdateFile = new Service.UpdateService.UpdateFile();
            firstUpdateFile.FilePath = "~\\" + xmlPathName;
            firstUpdateFile.Name = xmlPathName;
            firstUpdateFile.TargetFilePath = "~@App\\" + xmlPathName;
            updateFiles.Add(firstUpdateFile);

            XmlDocument xmlUpdateFile = new XmlDocument();
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.IgnoreComments = true;
            XmlReader reader = XmlReader.Create(dir, settings);
            xmlUpdateFile.Load(reader);

            XmlNode updateFileNodes = xmlUpdateFile.SelectSingleNode("Patch");
            if (updateFileNodes == null)
            {
                return null;
            }

            for (int i = 0; i < updateFileNodes.ChildNodes.Count; i++)
            {
                UpdateFile updateFile = new Service.UpdateService.UpdateFile();
                XmlNode c = updateFileNodes.ChildNodes[i];
                updateFile.Name = c.Attributes["Name"].Value;
                updateFile.FilePath = c.Attributes["Path"].Value;
                updateFile.TargetFilePath = c.Attributes["TargetPath"].Value;

                updateFiles.Add(updateFile);

            }
            reader.Close();
            return updateFiles;
        }

        #region
        public UploadResult UploadIncrementalPatchFileSegment(string product, 
            string appType,
            string fromVersion,
            string toVersion, 
            long start, 
            int length, 
            byte[] data)
        {
            string patchFilePath = PatchUtil.GetIncrementalPatchFileAbsolutePath(AppDomain.CurrentDomain.BaseDirectory,
                product,
                appType,
                fromVersion,
                toVersion);
            using (FileStream fileStream = new FileStream(patchFilePath, FileMode.OpenOrCreate))
            {
                fileStream.Seek(0, SeekOrigin.End);
                fileStream.Write(data, 0, length);
            }
            
            UploadResult result = new UploadResult();
            result.Start = start;
            result.Length = length;
            result.FileName = PatchUtil.GetIncrementalPatchFileName(fromVersion, toVersion);
            return result;
        }

        public UploadResult UploadFullPatchFileSegment(string product,
            string appType,
            string toVersion,
            long start,
            int length,
            byte[] data)
        {
            string patchFilePath = PatchUtil.GetFullPatchFileAbsolutePath(AppDomain.CurrentDomain.BaseDirectory,
                product,
                appType,
                toVersion);
            using (FileStream fileStream = new FileStream(patchFilePath, FileMode.OpenOrCreate))
            {
                fileStream.Seek(0, SeekOrigin.End);
                fileStream.Write(data, 0, length);
            }

            UploadResult result = new UploadResult();
            result.Start = start;
            result.Length = length;
            result.FileName = PatchUtil.GetFullPatchFileName(toVersion);
            return result;
        }

        /// <summary>
        /// 更新版本信息
        /// </summary>
        /// <param name="product"></param>
        /// <param name="appType"></param>
        /// <param name="version"></param>
        /// <param name="isNecessary"></param>
        /// <returns></returns>
        public UpdateVersionResult UpdateVersion(string product, string appType, string version, bool isNecessary)
        {
            UpdateVersionResult result = new UpdateVersionResult();

            string versionFile = PatchUtil.GetVersionFileAbsolutePath(AppDomain.CurrentDomain.BaseDirectory,
                product,
                appType);

            try
            {
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(versionFile);
                string curVersion = xmlDoc.SelectSingleNode("Version/LatestVersion").InnerText;

                if (new Version(version) > new Version(curVersion))
                {
                    XmlNode necessaryNode = xmlDoc.SelectSingleNode("Version/Necessary");
                    if (necessaryNode == null)
                    {
                        necessaryNode = xmlDoc.CreateElement("Necessary");
                        necessaryNode.InnerText = Convert.ToString(isNecessary);
                        xmlDoc.SelectSingleNode("Version").AppendChild(necessaryNode);
                    }
                    else
                    {
                        necessaryNode.InnerText = Convert.ToString(isNecessary);
                    }

                    xmlDoc.SelectSingleNode("Version/LatestVersion").InnerText = version;
                    xmlDoc.Save(versionFile);

                    result.IsSuccess = true;
                }
                else
                {
                    Common.Logger.WarningToTag("UpdateServer", "更新版本信息失败,product:{0},app:{1},version:{2},当前版本更大：{3}", product, appType, version, curVersion);

                    result.IsSuccess = false;
                }
            }
            catch(Exception ex)
            {
                Common.Logger.ErrorToTag("UpdateServer", "更新版本信息失败,product:{0},app:{1},version:{2},ex:{3}",product, appType, version, ex);
                result.IsSuccess = false;
            }

            return result;
        }
        #endregion


        public PatchInfo GetPackage(string product, string appType, string localVersion, PatchType patchType)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 检查版本信息
        /// </summary>
        /// <param name="product"></param>
        /// <param name="appType"></param>
        /// <param name="localVersion"></param>
        /// <returns></returns>
        public CheckVersionResult CheckVersion(string product, string appType, string localVersion)
        {
            CheckVersionResult result = new CheckVersionResult();

            string versionFile = PatchUtil.GetVersionFileAbsolutePath(AppDomain.CurrentDomain.BaseDirectory,
                product,
                appType);

            try
            {
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(versionFile);
                string curVersion = xmlDoc.SelectSingleNode("Version/LatestVersion").InnerText;
                result.LatestVersion = curVersion;

                XmlNode necessaryNode = xmlDoc.SelectSingleNode("Version/Necessary");
                if (necessaryNode != null)
                {
                    result.IsNecessary = Convert.ToBoolean(necessaryNode.InnerText);
                }

                Common.Logger.WarningToTag("UpdateServer", "检查版本,product:{0},app:{1},version:{2}, server：{3}",
                    product, appType, localVersion, curVersion);

                if (new Version(localVersion) >= new Version(curVersion))
                {
                    result.IsNeedUpdate = false;
                }
                else
                {
                    result.IsNeedUpdate = true;

                    string incrementalPatch = PatchUtil.GetIncrementalPatchFileAbsolutePath(AppDomain.CurrentDomain.BaseDirectory,
                        product,
                        appType,
                        localVersion,
                        curVersion);
                    string fullPatch = PatchUtil.GetFullPatchFileAbsolutePath(AppDomain.CurrentDomain.BaseDirectory,
                        product,
                        appType,
                        curVersion);
                    if (File.Exists(incrementalPatch))
                    {
                        result.HasIncrementalPatch = true;
                    }
                    else
                    {
                        result.HasIncrementalPatch = false;
                    }


                    if (File.Exists(fullPatch))
                    {
                        result.HasFullPatch = true;
                    }
                    else
                    {
                        result.HasFullPatch = false;
                    }
                }
            }
            catch (Exception ex)
            {
                Common.Logger.WarningToTag("UpdateServer", "检查版本出错,product:{0},app:{1},version:{2}, ex：{3}",
                    product, appType, localVersion, ex);
                result.IsNeedUpdate = false;
            }

            return result;
        }

        /// <summary>
        /// 下载增量包文件
        /// </summary>
        /// <param name="product"></param>
        /// <param name="appType"></param>
        /// <param name="fromVersion"></param>
        /// <param name="toVersion"></param>
        /// <param name="start"></param>
        /// <returns></returns>
        public DownloadResult DownloadIncrementalPatchFileSegment(string product,
            string appType,
            string fromVersion,
            string toVersion,
            long start)
        {
            DownloadResult result = new DownloadResult();
            string patchFilePath = PatchUtil.GetIncrementalPatchFileAbsolutePath(AppDomain.CurrentDomain.BaseDirectory,
                product,
                appType,
                fromVersion,
                toVersion);

            string patchFileName = PatchUtil.GetIncrementalPatchFileName(fromVersion, toVersion);

            if (!File.Exists(patchFilePath))
            {
                Common.Logger.WarningToTag("UpdateServer", "补丁文件:{0}不存在", patchFilePath);
                result.IsSuccess = false;
            }
            else
            {

                byte[] data = new byte[4096];

                using (FileStream fileStream = new FileStream(patchFilePath, FileMode.Open))
                {
                    fileStream.Seek(start, SeekOrigin.Begin);
                    int len = fileStream.Read(data, 0, data.Length);
                    result.IsSuccess = true;
                    result.TotalSize = fileStream.Length;
                    result.FileName = patchFileName;
                    result.Start = start;
                    result.Length = len;
                    result.Data = data;
                }
            }
            return result;
        }

        public DownloadResult DownloadFullPatchFileSegment(string product,
            string appType,
            string toVersion,
            long start)
        {
            DownloadResult result = new DownloadResult();
            string patchFilePath = PatchUtil.GetFullPatchFileAbsolutePath(AppDomain.CurrentDomain.BaseDirectory,
                product,
                appType,
                toVersion);

            string patchFileName = PatchUtil.GetFullPatchFileName(toVersion);

            if (!File.Exists(patchFilePath))
            {
                Common.Logger.WarningToTag("UpdateServer", "补丁文件:{0}不存在", patchFilePath);
                result.IsSuccess = false;
            }
            else
            {

                byte[] data = new byte[4096];

                using (FileStream fileStream = new FileStream(patchFilePath, FileMode.Open))
                {
                    fileStream.Seek(start, SeekOrigin.Begin);
                    int len = fileStream.Read(data, 0, data.Length);
                    result.IsSuccess = true;
                    result.TotalSize = fileStream.Length;
                    result.FileName = patchFileName;
                    result.Start = start;
                    result.Length = len;
                    result.Data = data;
                }
            }
            return result;
        }
    }
}
